##################################################################
# PyngFM is a Python implementation of Ping.fm API.
#
# Version: 1.0
# Author: Andrea Grandi <a _DOT_ grandi _AT_ gmail _DOT_ com>
# License: GPL2
# Website: http://pyngfm.googlecode.com
#
# Note: this code is inspired to PHPingFM project.
##################################################################
import urllib
import urllib2
from xml.etree import ElementTree as ET

from service import SystemService, UserService
from trigger import Trigger
from message import Message


class PingFMClient():
    # The developer's API key
    api_key = ''

    # User's app key
    user_app_key = ''

    def __init__(self, apikey = None, userappkey = None):
        self.api_key = apikey
        self.user_app_key = userappkey

    def set_user_app_key(self, key):
        '''
        Sets the User's APP key, if not already set.  It can also be used to
        change the key.
        '''
        self.user_app_key = key

    def set_api_key(self, key):
        '''
        Sets the API key, if not already set.  It can also be used to change
        the key.
        '''
        self.api_key = key

    def __callMethod(self, service, params):
        '''
        Call a method on the ping.fm server.

        @param $service
          The end of the URL to fetch.  For example user.services will
          become http://api.ping.fm/v1/user.services when requesting from the
          server.
        @param $fields
          The fields to pass in over POST.  The user's app key and the API key
          are automatically added.
        @return TODO: document me!
        '''
        base_url = 'http://api.ping.fm/v1/'
        url = base_url + str(service)

        if self.api_key != None:
            params['api_key'] = self.api_key

            if self.user_app_key != None:
                params['user_app_key'] = self.user_app_key

            parameters = urllib.urlencode(params)
            response = urllib2.urlopen(url, parameters).read()

            return response

    def user_validate(self):
        '''
        Public API function: user.validate: validates the
        given user's app key.

        @return
          A boolean of whether the app key is correct.
        '''
        validates = self.__callMethod('user.validate')
        data = ET.XML(validates)
        return (data.get('status') == "OK")

    def user_post(self, post_method, body, title=None, services=None,
                  location=None, media=None, encoding=None, exclude=None,
                  debug=None, checksum=None):
        '''
        Public API function: user.post: posts a message to the user's Ping.fm
        services.

        @param $post_method
          Posting method.  Either "blog", "microblog" or "status."
        @param $body
          Message body.
        @param $title
          Title of the posted message.  This will only appear if the specified
          service supports a title field.  Otherwise, it will be discarded.
        @param $services
          A single service or array of services to post to.
          Default is all services set up for specified method.  If the posted
          method is not supported by service, the request will return an error.
        '''
        parameters = {}

        parameters['post_method'] = post_method
        parameters['body'] = body

        if(title != None):
            parameters['title'] = title

        if(services != None):
            parameters['services'] = services

        if(location != None):
            parameters['location'] = location

        if(media != None):
            parameters['media'] = media

        if(encoding != None):
            parameters['encoding'] = encoding

        if(exclude != None):
            parameters['exclude'] = exclude

        if(debug != None):
            parameters['debug'] = debug

        if(checksum != None):
            parameters['checksum'] = checksum

        response = self.__callMethod('user.post', parameters)
        data = ET.XML(response)

        return data.get('status')

    def user_tpost(self, trigger, body, title=None, location=None, media=None,
                   encoding=None, exclude=None, debug=None, checksum=None):
        '''
        Public API function: user.tpost: posts a message to the user's Ping.fm
        services using one of their custom triggers.
        
        @param $trigger
          Custom trigger the user has defined from the Ping.fm website.
        @param $body
          Message body.
        @param $title
          Title of the posted message.  This will only appear if the specified
          service supports a title field.  Otherwise, it will be discarded.
        '''
        parameters = {}

        parameters['trigger'] = trigger
        parameters['body'] = body

        if(title != None):
            parameters['title'] = title

        if(location != None):
            parameters['location'] = location

        if(media != None):
            parameters['media'] = media

        if(encoding != None):
            parameters['encoding'] = encoding

        if(exclude != None):
            parameters['exclude'] = exclude

        if(debug != None):
            parameters['debug'] = debug

        if(checksum != None):
            parameters['checksum'] = checksum

        response = self.__callMethod('user.tpost', parameters)
        data = ET.XML(response)

        return data.get('status')

    def system_services(self):
        '''
        Public API function: system.services: Return a complete list of
        supported services.
        
        @return
          A list of SystemService objects containing all informations about
          Ping.fm supported services
        '''
        parameters = {}

        response = self.__callMethod('system.services', parameters)
        data = ET.XML(response)

        if(data.get('status') == "OK"):
            services = data.find('services')

            service_list = []

            for s in services:
                service = SystemService()

                service.set_id(s.attrib['id'])
                service.set_name(s.attrib['name'])
                service.set_trigger(s.find('trigger').text)
                service.set_url(s.find('url').text)
                service.set_icon(s.find('icon').text)

                service_list.append(service)

            return service_list

    def user_key(self, mobile_key):
        '''
        Public API function: user.key: Will exchange a mobile application key
        for a functional application key.  This is for mobile apps that would
        offer an easier way of authenticating users.
        
        @return
          A User Application's key
        '''
        parameters = {}
        parameters['mobile_key'] = mobile_key

        response = self.__callMethod('user.key', parameters)
        data = ET.XML(response)

        if(data.get('status') == "OK"):
            key_el = data.find('key')
            return key_el.text

    def user_services(self):
        '''
        Public API function: user.services: Returns a list of services the
        particular user has set up through Ping.fm.
        
        @return
          A list of UserService objects containing all informations about
          services the particular user has set up through Ping.fm.
        '''
        parameters = {}

        response = self.__callMethod('user.services', parameters)
        data = ET.XML(response)

        if(data.get('status') == "OK"):
            services = data.find('services')

            service_list = []

            for s in services:
                service = UserService()

                service.set_id(s.attrib['id'])
                service.set_name(s.attrib['name'])
                service.set_trigger(s.find('trigger').text)
                service.set_url(s.find('url').text)
                service.set_icon(s.find('icon').text)
                service.set_methods(s.find('methods').text.split(','))

                service_list.append(service)

            return service_list

    def user_triggers(self):
        '''
        Public API function: user.triggers: Returns a user's custom triggers.
        
        @return
          A list of Trigger objects containing all informations about
          user's custom triggers.
        '''
        parameters = {}

        response = self.__callMethod('user.triggers', parameters)
        data = ET.XML(response)

        if(data.get('status') == "OK"):
            triggers = data.find('triggers')

            trigger_list = []

            for t in triggers:
                trigger = Trigger()

                trigger.set_id(t.attrib['id'])
                trigger.set_method(t.attrib['method'])

                services = t.find('services')

                for s in services:
                    trigger.add_service(s.attrib['id'], s.attrib['name'])

                trigger_list.append(trigger)

            return trigger_list

    def user_latest(self, limit=25, order='DESC'):
        '''
        Public API function: user.latest: Returns the last 25 messages a
        user has posted through Ping.fm.
        
        @return
          A list of Message object containing the last messages a user has
          posted trough Ping.fm
        '''
        parameters = {}
        parameters['limit'] = limit
        parameters['order'] = order

        response = self.__callMethod('user.latest', parameters)
        data = ET.XML(response)

        print response

        if(data.get('status') == "OK"):
            messages = data.find('messages')

            message_list = []

            for m in messages:
                message = Message()

                message.set_id(m.attrib['id'])
                message.set_method(m.attrib['method'])

                date = m.find('date')

                message.set_rfc_time(date.attrib['rfc'])
                message.set_unix_time(date.attrib['unix'])

                services = m.find('services')

                for s in services:
                    message.add_service(s.attrib['id'], s.attrib['name'])

                content = m.find('content')

                for c in content:
                    if c.tag == 'body':
                        message.set_body(c.text)
                    if c.tag == 'title':
                        message.set_title(c.text)

                location = m.find('location')

                if location != None:
                    message.set_location(location.text)

                message_list.append(message)

            return message_list
